package net.track.gps;

import java.io.File;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

public class GPXTrack {

//    private String gpsFileName = null;
    private File gpsFile = null;
    private String trackName = "Unknow track";
    private int nbPoints = 0;
    private float positiveElevation = 0;
    private float negativeElevation = 0;
    private float maxElevation = -10000; // in case we start in the ocean
    private float minElevation = 10000; // in case we fly

    /**
     * Constructor
     * @param fileName
     */
    public GPXTrack(String fileName) {
//        gpsFileName = fileName;
        gpsFile = new File(fileName);
    }
    /**
     *
     * @param file
     */
    public GPXTrack(File file){
        gpsFile = file;
    }

    /**
     * @param args
     */
    public static void main(String args[]) {

        try {
            GPXTrack track = new GPXTrack(args[0]);
            track.checkElevation();
            System.out.println(track);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("Please provide a GPS file name");
        }
    }

    /**
     *
     * Parse GPS File (XML) with the following structure
     * Calculate mx elevation, min elevation, positive elevation and negative elevation
     *
    <gpx xmlns="http://www.topografix.com/GPX/1/1" creator="BaseCamp 2.0.8" version="1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">

    <metadata>
    <link href="http://www.garmin.com">
    <text>Garmin International</text>
    </link>
    <time>2010-04-22T10:21:40Z</time>
    <bounds maxlat="42.5662524" maxlon="2.1028623" minlat="42.4994435" minlon="1.9937591"/>
    </metadata>

    <trk>
    <name>D1-Bouillouses-Matemale</name>
    <extensions>
    <gpxx:TrackExtension xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3">
    <gpxx:DisplayColor>Yellow</gpxx:DisplayColor>
    </gpxx:TrackExtension>
    </extensions>
    <trkseg>
    <trkpt lat="42.5119290" lon="2.0459985">
    <ele>1827.7968750</ele>
    </trkpt>
    <trkpt lat="42.5122374" lon="2.0455563">
    <ele>1833.8867188</ele>
    </trkpt>
    <trkpt lat="42.5128770" lon="2.0454083">
    <ele>1832.7773438</ele>
    </trkpt>
    <trkpt lat="42.5131710" lon="2.0445585">
    <ele>1828.2304688</ele>
    </trkpt>
    <trkpt lat="42.5139787" lon="2.0441055">
    <ele>1831.3046875</ele>
    </trkpt>
    <trkpt lat="42.5149935" lon="2.0427753">
    <ele>1838.9921875</ele>
    </trkpt>
     */
    public void checkElevation() {
        try {
//            File file = new File(gpsFileName);
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document doc = db.parse(gpsFile);
            // get root element
            Element root = doc.getDocumentElement();

            // get track element
            NodeList trkLst = root.getElementsByTagName("trk");
            Element trk = (Element) trkLst.item(0);

            // get track name
            setTrackName(getTextValue(trk, "name"));


            //get segment part
            NodeList segList = trk.getElementsByTagName("trkseg");
            Element segment = (Element) segList.item(0);
            if (segment != null) {
                // get all points
                NodeList pointsLst = segment.getElementsByTagName("trkpt");
                setNbPoints(pointsLst.getLength());
                float previous = 0;
                for (int i = 0; i < pointsLst.getLength(); i++) {
                    Element point = (Element) pointsLst.item(i);
                    float ele = getFloatValue(point, "ele");
                    // check max elevation
                    if (ele > getMaxElevation()) {
                        setMaxElevation(ele);
                    }
                    // check min elevation
                    if (ele < getMinElevation()) {
                        setMinElevation(ele);
                    }
                    if (i == 0) {
                        // previous = first one
                        previous = ele;
                    } else {
                        // increment positive and negative elevation
                        float delta = ele - previous;
                        if (delta > 0) {
                            setPositiveElevation(getPositiveElevation() + delta);
                        } else {
                            setNegativeElevation(getNegativeElevation() + delta);
                        }
                        previous = ele;
                    }
                }
            }

        } catch (Exception e) {
            System.out.println("Error during GPS file parsing ....");
            e.printStackTrace();
        }
    }

    /**
     * Return value of a tag as a String
     * @param e
     * @param tagName
     * @return
     */
    private String getTextValue(Element e, String tagName) {
        String value = null;
        NodeList nl = e.getElementsByTagName(tagName);
        if (nl != null && nl.getLength() > 0) {
            Element el = (Element) nl.item(0);
            value = el.getFirstChild().getNodeValue();
        }

        return value;
    }

    /**
     * Returns value of a tag as a float
     * @param e
     * @param tagName
     * @return
     */
    private float getFloatValue(Element e, String tagName) {
        float value = 0;
        NodeList nl = e.getElementsByTagName(tagName);
        if (nl != null && nl.getLength() > 0) {
            Element el = (Element) nl.item(0);
            value = Float.valueOf(el.getFirstChild().getNodeValue()).floatValue();
        }

        return value;
    }

    /**
     * Override toStrung behavior
     */
    public String toString() {

        return "Track name: " + getTrackName() + "\n"
                + "Number of points: " + getNbPoints() + "\n"
                + "Min Elevation: " + getMinElevation() + "\n"
                + "Max Elevation: " + getMaxElevation() + "\n"
                + "Negative Elevation: " + getNegativeElevation() + "\n"
                + "Positive Elevation: " + getPositiveElevation();
    }

    /**
     * @return the trackName
     */
    public String getTrackName() {
        return trackName;
    }

    /**
     * @param trackName the trackName to set
     */
    public void setTrackName(String trackName) {
        this.trackName = trackName;
    }

    /**
     * @return the nbPoints
     */
    public int getNbPoints() {
        return nbPoints;
    }

    /**
     * @param nbPoints the nbPoints to set
     */
    public void setNbPoints(int nbPoints) {
        this.nbPoints = nbPoints;
    }

    /**
     * @return the positiveElevation
     */
    public float getPositiveElevation() {
        return positiveElevation;
    }

    /**
     * @param positiveElevation the positiveElevation to set
     */
    public void setPositiveElevation(float positiveElevation) {
        this.positiveElevation = positiveElevation;
    }

    /**
     * @return the negativeElevation
     */
    public float getNegativeElevation() {
        return negativeElevation;
    }

    /**
     * @param negativeElevation the negativeElevation to set
     */
    public void setNegativeElevation(float negativeElevation) {
        this.negativeElevation = negativeElevation;
    }

    /**
     * @return the maxElevation
     */
    public float getMaxElevation() {
        return maxElevation;
    }

    /**
     * @param maxElevation the maxElevation to set
     */
    public void setMaxElevation(float maxElevation) {
        this.maxElevation = maxElevation;
    }

    /**
     * @return the minElevation
     */
    public float getMinElevation() {
        return minElevation;
    }

    /**
     * @param minElevation the minElevation to set
     */
    public void setMinElevation(float minElevation) {
        this.minElevation = minElevation;
    }
}
